#include "precompile.h"

#include "LoadingManager.h"
#include "ObjectModel.h"

LoadingManager::LoadingManager(const String& cfgFileName,ObjectModel* system)
	: m_cfgFileName(cfgFileName),
	m_system(system)
{
	InitCategories();
}

LoadingManager::~LoadingManager()
{
	for (ComponentLoaderThreadPool::iterator iter = m_loaderThreadPool.begin(); iter != m_loaderThreadPool.end(); iter++)
	{
		delete iter->second;
		iter->second = NULL;
	}
}

void LoadingManager::InitCategories()
{
	KeyValuePairArray arrCategoryInfo;
	StringArray arrCategories;
	if (GetProfileKeyNamesBySection(m_cfgFileName, L"Categories", arrCategories))
	{
		String wsCategoryName = L"";
		StringArray arrComponents;
		for (unsigned i = 0; i < arrCategories.size(); i++)
		{
			wsCategoryName = arrCategories[i];
			if (GetProfileKeyNamesBySection(m_cfgFileName, wsCategoryName, arrComponents))
			{
				ComponentCategory category(wsCategoryName);
				for (unsigned j = 0; j < arrComponents.size(); j++)
					category.AddOneComponent(arrComponents[j]);
				m_mapComponentCategories[wsCategoryName] = category;
			}
		}
	}
}

bool LoadingManager::LoadAllCategories()
{
	ComponentCategoryMap::iterator itbegin = m_mapComponentCategories.begin();
	ComponentCategoryMap::iterator itend = m_mapComponentCategories.end();
	for (; itbegin != itend; ++itbegin)
	{
		ComponentCategory& category = itbegin->second;
		if (!LoadCategory(category.GetName()))
			return false;
	}

	return true;
}

bool LoadingManager::LoadCategory(const String& wsCategoryName)
{
	if (IsCategoryLoaded(wsCategoryName))
		return true;

	ComponentCategoryMap::iterator iter = m_mapComponentCategories.find(wsCategoryName);
	if (iter == m_mapComponentCategories.end())
		return false;

	const StringArray& arrComponents = iter->second.GetComponents();
	for (unsigned i = 0; i < arrComponents.size(); i++)
	{
		LoadComponentDirectly(arrComponents[i].c_str());
	}

	iter->second.SetLoadFlag(true);

	return true;
}

bool LoadingManager::AsyncLoadCategory(const String& wsCategoryName)
{
	if (IsCategoryLoaded(wsCategoryName))
		return true;

	ComponentCategoryMap::iterator iter = m_mapComponentCategories.find(wsCategoryName);
	if (iter == m_mapComponentCategories.end())
		return false;

	InvokeNewLoader(iter->second.GetName());

	return true;
}

bool LoadingManager::UnloadCategory(const String& wsCategoryName)
{
	UNREFERENCED_PARAMETER(wsCategoryName);

	ASSERT(FALSE);		// <---- Not implemented yet
	return true;
}

void LoadingManager::NotifyLoaderThreadFinished(const String& wsCategoryName)
{
	m_mapComponentCategories[wsCategoryName].SetLoadFlag(true);
}

void LoadingManager::LoadComponentDirectly(const String& wsComponentName)
{
	m_system->RegisterComponent(wsComponentName.c_str());
}

bool LoadingManager::GetProfileKeyNamesBySection(const String& m_cfgFileName, 
														  const String& wsSectionName, 
														  StringArray& arrKeyNames)
{
	uint32 dwSize = 4096;
	uint32 dwRetSize = dwSize;
	wchar_t* pwsBuf = NULL;
	while (dwRetSize >= dwSize - sizeof(wchar_t))
	{
		pwsBuf = new wchar_t[dwSize];
		::wmemset(pwsBuf, 0, dwSize);
		dwRetSize = ::GetPrivateProfileSection(wsSectionName.c_str(), pwsBuf, dwSize, m_cfgFileName.c_str());
		delete[] pwsBuf;
		pwsBuf = NULL;
		dwSize += 256;
	}

	arrKeyNames.clear();
	wchar_t* pw = pwsBuf;
	while (*pw != L'\0')
	{
		String wsKey(pw); 
		unsigned i = wsKey.find(L'=');
		if (i != String::npos)
			wsKey = wsKey.substr(0, i);
		arrKeyNames.push_back(wsKey);
		pw += ::wcslen(pw) + 1;
	}
	delete[] pwsBuf;

	return (arrKeyNames.size() > 0);
}

void LoadingManager::InvokeNewLoader(const String& wsCategoryName)
{
	LoadingThread* pLoaderThread = new LoadingThread(this, wsCategoryName);
	m_loaderThreadPool[wsCategoryName] = pLoaderThread;
	pLoaderThread->Start();
}

void LoadingManager::RemoveLoader(const String& wsCategoryName)
{
	ComponentLoaderThreadPool::iterator iter = m_loaderThreadPool.find(wsCategoryName);
	if (iter != m_loaderThreadPool.end())
	{
		delete iter->second;
		m_loaderThreadPool.erase(iter);
	}
}